package dev.icerock.moko.resources.utils
import dev.icerock.moko.resources.interop.ohosutils.*
import kotlinx.cinterop.*
import kotlinx.cinterop.internal.CStruct
import platform.posix.free
import platform.posix.memcpy
import platform.posix.size_tVar
import platform.posix.uint32_tVar
import platform.posix.uint64_tVar

internal object OhosResourceUtils {
    @OptIn(ExperimentalForeignApi::class)
    fun getString(resName: String): String? {
        val stringVar = nativeHeap.alloc<CPointerVar<ByteVar>>()
        try {
            val code = GetStringByName(resName, stringVar.ptr)
            if (code != 0) {
                return null
            }
            return stringVar.value?.toKString()
        } finally {
            stringVar.value?.let {
                free(it)
            }
            nativeHeap.free(stringVar)
        }
    }

    @OptIn(ExperimentalForeignApi::class)
    fun getString(resName: String, list: List<Any>): String? = memScoped {
        val out = alloc<CPointerVar<ByteVar>>()
        val cArgs = if (list.size > 0) allocArray<Arg>(list.size)  else null
        list.forEachIndexed { idx, a->
            val slot = cArgs?.get(idx)
            when(a) {
                is Int -> {
                    slot?.type = ArgType.ARG_INT
                    slot?.v?.i = a
                }
                is Float -> {
                    slot?.type = ArgType.ARG_FLOAT
                    slot?.v?.f = a
                }
                is Double -> {
                    slot?.type = ArgType.ARG_FLOAT
                    slot?.v?.f =a.toFloat()
                }
                is String -> {
                    slot?.type = ArgType.ARG_STR
                    slot?.v?.s = a.cstr.getPointer(this)
                }
                else -> {
                    error("unsupported arg type: ${a::class}")
                }
            }
        }
        val rc = GetFormatStringByName(resName, out.ptr, cArgs?.reinterpret(), list.size)
        if (rc != 0) return@memScoped null
        val ret = out.value?.toKString()
        free(out.value)
        ret
    }

    @OptIn(ExperimentalForeignApi::class)
    fun getPlural(resName: String, args: UInt): String? {
        val pluralVar = nativeHeap.alloc<CPointerVar<ByteVar>>()
        try {
            val code = GetStringPluralByName(resName, args, pluralVar.ptr)
            if (code != 0) {
                return null
            }
            return pluralVar.value?.toKString()
        } finally {
            pluralVar.value?.let {
                free(it)
            }
            nativeHeap.free(pluralVar)
        }
    }

    @OptIn(ExperimentalForeignApi::class)
    fun getPlural(resName: String, args: UInt, list: List<Any>): String? = memScoped {
        if (list.size > 5) {
            throw RuntimeException("unsupported var param beyound 5")
        }
        val out = alloc<CPointerVar<ByteVar>>()
        val cArgs = if (list.size > 0) allocArray<Arg>(list.size)  else null
        list.forEachIndexed { idx, a->
            val slot = cArgs?.get(idx)
            when(a) {
                is Int -> {
                    slot?.type = ArgType.ARG_INT
                    slot?.v?.i = a
                }
                is Float -> {
                    slot?.type = ArgType.ARG_FLOAT
                    slot?.v?.f = a
                }
                is Double -> {
                    slot?.type = ArgType.ARG_FLOAT
                    slot?.v?.f =a.toFloat()
                }
                is String -> {
                    slot?.type = ArgType.ARG_STR
                    slot?.v?.s = a.cstr.getPointer(this)
                }
                else -> {
                    error("unsupported arg type: ${a::class}")
                }
            }
        }
        val rc = GetFormatStringPluralByName(resName, args, out.ptr, cArgs?.reinterpret(), list.size)
        if (rc != 0) return@memScoped null
        val ret = out.value?.toKString()
        free(out.value)
        ret
    }

    @OptIn(ExperimentalForeignApi::class)
    fun getImage(resName: String): ByteArray? {
        val imageLenVar = nativeHeap.alloc<uint64_tVar>()
        val imageVar = nativeHeap.alloc<CPointerVar<ByteVar>>()
        val density: UInt = 0u
        try {
            val code = GetMediaByName(resName, imageVar.ptr, imageLenVar.ptr, density)
            if (code != 0) {
                return null
            }
            return ByteArray(imageLenVar.value.toInt()).apply {
                usePinned { pinned ->
                    memcpy(pinned.addressOf(0), imageVar.value, imageLenVar.value)
                }
            }
        } finally {
            imageVar.value?.let { free(it) }
            nativeHeap.free(imageVar)
            nativeHeap.free(imageLenVar)
        }
    }
    @OptIn(ExperimentalForeignApi::class)
    fun getColor(resName: String): Int? {
        val colorVar = nativeHeap.alloc<uint32_tVar>()
        val code = GetColorByName(resName, colorVar.ptr)
        try {
            if (code != 0) {
                return null
            }
            return colorVar.value.toInt()
        } finally {
            nativeHeap.free(colorVar)
        }

    }
    @OptIn(ExperimentalForeignApi::class)
    fun getFile(resName: String): ByteArray? {
            val fontDataVar = nativeHeap.alloc<CPointerVar<ByteVar>>()
            val fontLenVar = nativeHeap.alloc<size_tVar>()
            try {
                val code = GetFileByName(resName, fontDataVar.ptr, fontLenVar.ptr)
                if (code != 0) {
                    return null
                }
                return ByteArray(fontLenVar.value.toInt()).apply {
                    usePinned { pinned ->
                        memcpy(pinned.addressOf(0), fontDataVar.value, fontLenVar.value)
                    }
                }
            } finally {
                fontDataVar.value?.let { free(it) }
                nativeHeap.free(fontDataVar)
                nativeHeap.free(fontLenVar)
            }
    }

    fun getFont(resName: String): ByteArray? {
        return getFile(resName)
    }
}